using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

using Box2D.XNA;

namespace G4K
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public const int NUM_BEANS = 20;
        public const int WIDTH = 800;
        public const int HEIGHT = 480;

        struct CBean
        {
            public BEANTYPE type;
            public Vector2 origin;
            public Vector2 pos { get { return physbody.GetPosition(); } }
            public float angle { get { return physbody.GetAngle(); } }

            public Body physbody;
        }

        enum TEXID
        {
            BACKGROUND = 0,
            MAP = 1,
            BEANRED = 2,
            BEANGREEN = 3,
            BEANBLUE = 4,
            BEANWHITE = 5,
            BEANSHADOW = 6,
        };

        enum BEANTYPE
        {
            RED = 2,
            GREEN = 3,
            BLUE = 4,
            WHITE = 5,
        };

        string[] mTexNames = 
        {
            "background",
            "map",
            "beanred",
            "beangreen",
            "beanblue",
            "beanwhite",
            "beanshadow"
        };

        Texture2D[] mTextures;

        CBean[] mBeans;
        
        // map data
        Color[] mMapData;
        // physic world
        World mWorld;
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            
            TargetElapsedTime = TimeSpan.FromTicks(333333);
        }

        protected override void Initialize()
        {
            mWorld = new World(new Vector2(0, 100), false);
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load textures
            mTextures = new Texture2D[mTexNames.Length];
            for (int i = 0; i < mTexNames.Length; i++)
            {
                mTextures[i] = Content.Load<Texture2D>("Images\\" + mTexNames[i]);
            }

            // Create physic-border-box
            {
                Vector2 topleft = new Vector2(0, 0);
                Vector2 bottomleft = new Vector2(0, HEIGHT);
                Vector2 topright = new Vector2(WIDTH, 0);
                Vector2 bottomright = new Vector2(WIDTH, HEIGHT);

                PolygonShape[] sborders = new PolygonShape[4];
                sborders[0] = new PolygonShape();
                sborders[1] = new PolygonShape();
                sborders[2] = new PolygonShape();
                sborders[3] = new PolygonShape();

                sborders[0].SetAsEdge(topleft, topright);
                sborders[1].SetAsEdge(bottomleft, bottomright);
                sborders[2].SetAsEdge(topleft, bottomleft);
                sborders[3].SetAsEdge(topright, bottomright);

                Body bborder = mWorld.CreateBody(new BodyDef());
                bborder.CreateFixture(sborders[0], 0);
                bborder.CreateFixture(sborders[1], 0);
                bborder.CreateFixture(sborders[2], 0);
                bborder.CreateFixture(sborders[3], 0);

            }

            // Create beans
            Random rand = new Random();
            mBeans = new CBean[NUM_BEANS];

            float haftWidth = mTextures[(int)TEXID.BEANBLUE].Width / 2;
            float haftHeight = mTextures[(int)TEXID.BEANBLUE].Height / 2;
            Vector2 origin = new Vector2(haftWidth, haftHeight);

            CircleShape shapeBean = new CircleShape();
            shapeBean._radius = 30;

            for (int i = 0; i < NUM_BEANS; i++)
            {
                mBeans[i] = new CBean();
                mBeans[i].type = (BEANTYPE)(rand.Next(4) + 2);
                mBeans[i].origin = origin;

                BodyDef bodydef = new BodyDef();
                bodydef.type = BodyType.Dynamic;
                bodydef.position = new Vector2(
                    rand.Next(WIDTH - (int)haftWidth * 2) + haftWidth,
                    rand.Next(HEIGHT - (int)haftWidth * 2) + haftWidth);
                bodydef.angle = MathHelper.ToRadians(rand.Next(360));

                mBeans[i].physbody = mWorld.CreateBody(bodydef);
                Fixture fixtr = mBeans[i].physbody.CreateFixture(shapeBean, 10);
                fixtr.SetFriction(100);
            }

            // Create data for final map.
            mMapData = new Color[mTextures[(int)TEXID.MAP].Width * mTextures[(int)TEXID.MAP].Height];

            mTextures[(int)TEXID.MAP].GetData<Color>(mMapData);

        }

        protected override void UnloadContent()
        {
            Content.Unload();
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            int pick = -1;
            Vector2 locPos = Vector2.Zero;
            foreach (TouchLocation loc in TouchPanel.GetState())
            {
                if (loc.State.Equals(TouchLocationState.Moved) ||
                    loc.State.Equals(TouchLocationState.Pressed))
                {
                    for (int i = NUM_BEANS - 1; i >= 0; i--)
                    {
                        if (isInside(loc.Position, mBeans[i]))
                        {
                            pick = i;
                            locPos = loc.Position;
                            break;
                        }
                    }
                }
            }

            if (pick != -1)
            {
                CBean bean = mBeans[pick];

                for (int j = pick; j < NUM_BEANS - 1; j++)
                {
                    mBeans[j] = mBeans[j + 1];
                }
                mBeans[NUM_BEANS - 1] = bean;
                bean.physbody.SetTransform(locPos, bean.angle);
            }

            // Update Physic World
            mWorld.Step((float)gameTime.ElapsedGameTime.TotalSeconds, 1, 1);

            
            System.Console.Out.WriteLine(gameTime.ElapsedGameTime.TotalSeconds.ToString());

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(mTextures[(int)TEXID.BACKGROUND], Vector2.Zero, Color.White);
            foreach (CBean bean in mBeans)
            {
                spriteBatch.Draw(mTextures[(int)TEXID.BEANSHADOW], bean.pos, null,
                    Color.White, 0, bean.origin, 1, SpriteEffects.None, 1);
                spriteBatch.Draw(mTextures[(int)bean.type], bean.pos, null,
                    Color.White, 0, bean.origin, 1, SpriteEffects.None, 1);
            }



            spriteBatch.End();

            base.Draw(gameTime);
        }

        private bool isInside(Vector2 p, CBean bean)
        {
            Vector2 p2 = bean.pos - bean.origin;
            if (p.X > p2.X && p.X < bean.pos.X + bean.origin.X &&
                p.Y > p2.Y && p.Y < bean.pos.Y + bean.origin.Y)
            {
                return true;
            }
            return false;
        }
    }
}
